package com.bpmn.flow.behavior;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bpmn.api.BpmTaskCallbackReqDTO;
import com.bpmn.common.constant.ApprovalModeConstants;
import com.bpmn.common.constant.BpmConstants;
import com.bpmn.common.constant.BpmnModelConstants;
import com.bpmn.common.enums.TaskOperationEnum;
import com.bpmn.common.enums.UserTaskApproveTypeEnum;
import com.bpmn.flow.controller.form.ModelMetaInfoForm;
import com.bpmn.utils.FlowableUtils;
import com.bpmn.utils.ModelUtils;
import com.bpmn.utils.QueryUtils;
import com.bpmn.utils.TaskConfigUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Component;

import java.util.*;

import static com.bpmn.common.constant.BpmConstants.*;

/**
 * 实现任务的候选人的计算
 *
 * @author rain
 */
@Slf4j
@Component
public class BpmTaskCandidateInvoker {

    /**
     * 计算任务的候选人
     *
     * @param execution 执行任务
     * @return 用户编号集合
     */
    public Set<String> calculateUsersByTask(DelegateExecution execution) {
        // 注意：解决极端情况下，Flowable 异步调用，导致租户 id 丢失的情况
        return FlowableUtils.execute(execution.getTenantId(), () -> {
            Set<String> result = new HashSet<>();
            // 审批类型非人工审核时，不进行计算候选人。原因是：后续会自动通过、不通过
            FlowElement flowElement = execution.getCurrentFlowElement();
            String emptyStrategy = ModelUtils.getUserTaskExtension(flowElement, BpmnModelConstants.EMPTY_STRATEGY);
            if (StrUtil.equalsAny(emptyStrategy, UserTaskApproveTypeEnum.AUTO_PASS.getType(), UserTaskApproveTypeEnum.AUTO_REJECT.getType())) {
                return result;
            }
            //获取模型元数据中获取用户接口地址
            ModelMetaInfoForm metaInfo = ModelUtils.getCallbackUrl(execution.getProcessDefinitionId());
            String userFetchUrl = metaInfo.getUserFetchUrl();
            ProcessInstance processInstance = QueryUtils.instanceQuery(execution.getProcessInstanceId()).singleResult();
            // 计算任务的候选人
            //如果是申请节点，则直接返回
            if (StrUtil.equals(BpmConstants.APPLY_USER_TASK_KEY, execution.getCurrentActivityId())) {
                return Set.of(processInstance.getStartUserId());
            }
            // 构造回调请求参数
            BpmTaskCallbackReqDTO taskCallbackReqDTO = getBpmTaskCallbackReqDTO(execution, processInstance);
            // 处理服务回调
            String jsonStr = JSONUtil.toJsonStr(taskCallbackReqDTO);
            //用户回调处理
            log.info("开始回调，回调地址{}，回调参数{}", userFetchUrl, jsonStr);
            try (HttpResponse response = HttpRequest.post(userFetchUrl).body(jsonStr).execute()) {
                String callbackResult = response.body();
                log.info("结束回调，回调地址{}，回调参数{}，回调结果{}", userFetchUrl, jsonStr, callbackResult);
                JSONObject jsonObject = JSONUtil.parseObj(callbackResult);
                return Optional.ofNullable(jsonObject.get("data")).map(o -> Convert.toSet(String.class, o)).orElse(result);
            } catch (Exception e) {
                log.error("用户回调失败", e);
            }
            return result;
        });
    }

    private static BpmTaskCallbackReqDTO getBpmTaskCallbackReqDTO(DelegateExecution execution, ProcessInstance processInstance) {
        BpmTaskCallbackReqDTO taskCallbackReqDTO = new BpmTaskCallbackReqDTO();
        taskCallbackReqDTO.setTaskName(execution.getCurrentFlowElement().getName());
        taskCallbackReqDTO.setTaskDefinitionKey(execution.getCurrentActivityId());
        taskCallbackReqDTO.setEventName(execution.getEventName());
        taskCallbackReqDTO.setProcessInstanceId(execution.getProcessInstanceId());
        taskCallbackReqDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
        taskCallbackReqDTO.setBusinessKey(processInstance.getBusinessKey());
        taskCallbackReqDTO.setStartUserName(processInstance.getStartUserId());
        return taskCallbackReqDTO;
    }

    /**
     * 初始化会签变量
     */
    protected void initMultiInstance(DelegateExecution multiInstanceRootExecution, int nrOfInstances, TaskConfigUtils.TaskConfig taskConfig) {
        //设置会签总数（用作条件判断）
        setVariable(multiInstanceRootExecution, MULTI_NUM_OF_INSTANCES, nrOfInstances);
        setVariable(multiInstanceRootExecution, MULTI_AGREE_COUNT, 0);
        setVariable(multiInstanceRootExecution, MULTI_REFUSE_COUNT, 0);
        setVariable(multiInstanceRootExecution, MULTI_ABSTAIN_COUNT, 0);
        if (Objects.nonNull(taskConfig)) {
            String approvalMode = taskConfig.getApprovalMode();
            // 根据审批模式处理任务分配
            if (ApprovalModeConstants.EXTERNAL.equals(approvalMode)) {
                @SuppressWarnings("unchecked")
                Set<String> assigneeUserIds = (Set<String>) multiInstanceRootExecution.getVariable(BpmnModelConstants.MULTI_INSTANCE_ASSIGNEES_COLLECTION, Set.class);
                if (assigneeUserIds == null) {
                    assigneeUserIds = calculateUsersByTask(multiInstanceRootExecution);
                    if (CollUtil.isEmpty(assigneeUserIds)) {
                        assigneeUserIds = Collections.emptySet();
                    }
                    setVariable(multiInstanceRootExecution, BpmnModelConstants.MULTI_INSTANCE_ASSIGNEES_COLLECTION, assigneeUserIds);
                }
            }
        }
    }

    /**
     * 设置会签结果变量
     */
    protected void setMultiInstanceResult(DelegateExecution miRootExecution, TaskOperationEnum operationTypeEnum) {
        int multiNumOfInstances = getVariable(miRootExecution, MULTI_NUM_OF_INSTANCES);
        int multiAgreeCount = getVariable(miRootExecution, MULTI_AGREE_COUNT);
        int multiRefuseCount = getVariable(miRootExecution, MULTI_REFUSE_COUNT);
        int multiAbstainCount = getVariable(miRootExecution, MULTI_ABSTAIN_COUNT);
        log.info("当前操作：{}，投票前：会签人数：{}，同意：{}，拒绝：{}，弃权：{}", operationTypeEnum.getDesc(), multiNumOfInstances, multiAgreeCount, multiRefuseCount, multiAbstainCount);
        switch (operationTypeEnum) {
            case MULTI_AGREE -> {
                multiAgreeCount++;
                setVariable(miRootExecution, MULTI_AGREE_COUNT, multiAgreeCount);
            }
            case MULTI_REFUSE -> {
                multiRefuseCount++;
                setVariable(miRootExecution, MULTI_REFUSE_COUNT, multiRefuseCount);
            }
            case MULTI_ABSTAIN -> {
                multiAbstainCount++;
                setVariable(miRootExecution, MULTI_ABSTAIN_COUNT, multiAbstainCount);
            }
        }
        log.info("当前操作：{}，投票后：会签人数：{}，同意：{}，拒绝：{}，弃权：{}", operationTypeEnum.getDesc(), multiNumOfInstances, multiAgreeCount, multiRefuseCount, multiAbstainCount);
    }


    protected void setVariable(DelegateExecution execution, String variableName, Object value) {
        execution.setVariable(variableName, value);
    }

    protected Integer getVariable(DelegateExecution execution, String variableName) {
        Object value = execution.getVariable(variableName);
        return (Integer) (value != null ? value : 0);
    }

}